#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <stropts.h>
#include <sys/ioctl.h>
#include <pthread.h>


#include "global.h"
#include "constantes.h"
#include "listas.h"
#include "sockets.h"
#include "configuracion.h"
#include "NIPC.h"
#include "hiloDisco.h"

int AgregarAVectorDiscosDeArchivoConfig( char *sNombre_NuevoDA ) ;
int  VerificarCompatibilidad(char *sNombre_NuevoDA , int iCC_NuevoDA, int iHH_NuevoDA , int iSS_NuevoDA);
int VerificarSITodosLosDAEstanListos();
int ConsolaEsperaNuevosDiscos (void * pSocketParaArray);

chs glChs;
extern ListConfig * listaConfig;

int ConsolaEsperaNuevosDiscos (void * pSocketParaArray)
{

	pthread_t ID_HiloDeDisco;
	int iSocketEscuchaConexiones,iSocketDisco_temporal;
	struct DiscoConocido *pNodoDisco,*pAuxDisco;
	char *sNombre_NuevoDA;
	fd_set set_socketConsola;
	InfoIPC infoIPC;
	int iCC_NuevoDA,iHH_NuevoDA,iSS_NuevoDA;
	struct sockaddr_in* cliente;
	int address_size=sizeof(struct sockaddr_in);
	pthread_t syncer_id;
	struct DiscoConocido *pDiscoALeer; 
   
	listaConfig = NULL;
	cargarConfig();
	iSocketEscuchaConexiones  = PrepararSocket (atoi(obtenerValor("puertoConex")),obtenerValor("ipLocal"));   
	escuchar(iSocketEscuchaConexiones,10); 

	while(1)
	{
		printf("\nEstado del Array:\r\n");
		switch(Estado)
		{
			case INICIALIZADO:
				printf("INICIALIZADO\r\n");
			break;

			case FUERA_DE_SERVICIO:
				printf("FUERA DE SERVICIO\r\n");
			break;
		}
		switch(SubEstado)
		{
			case SINCRONIZADO:
				printf("SINCRONIZADO\r\n");
			break;

			case INCOMPLETO:
				printf("INCOMPLETO\r\n");
			break;

			case SINCRONIZANDO:
				printf("SINCRONIZANDO\r\n");
			break;
		}
		
		printf("DISCOS CONECTADOS:\n");
		printf("------------------\n");
		pAuxDisco = ListaDiscos;
		if(pAuxDisco == NULL)
			printf("No hay\n");
		while(pAuxDisco != NULL)
		{
          printf("%s\n", pAuxDisco->sNombre );
          pAuxDisco = pAuxDisco->Sgt;	
		} 
		
		FD_ZERO(&set_socketConsola);
		FD_SET(iSocketEscuchaConexiones ,&set_socketConsola);
		select(iSocketEscuchaConexiones+1, &set_socketConsola, NULL, NULL, NULL);	

		if( FD_ISSET(iSocketEscuchaConexiones , &set_socketConsola ) > 0 )
		{
            if((iSocketDisco_temporal = accept(iSocketEscuchaConexiones,(struct sockaddr*)&cliente,&address_size))== -1) 
               return ERRORACCEPT;	
            
            printf("\nSe conecto un disco.El descriptor temporal devuelto es: %i \n", iSocketDisco_temporal);     
            		
			/*Handshake de disco*/
			recibirIPC(iSocketDisco_temporal,&infoIPC);
            sNombre_NuevoDA=(char *)malloc(sizeof(char));
            strcpy(sNombre_NuevoDA,infoIPC.payload);
			infoIPC.payLoadLength=5;
			generarStructIPC("nada",HSOK,&infoIPC);
			enviarIPC(iSocketDisco_temporal,&infoIPC);
			recibirIPC(iSocketDisco_temporal,&infoIPC);
            iCC_NuevoDA = atoi(infoIPC.payload);
            recibirIPC(iSocketDisco_temporal,&infoIPC);
            iHH_NuevoDA = atoi(infoIPC.payload);
            recibirIPC(iSocketDisco_temporal,&infoIPC);
            iSS_NuevoDA = atoi(infoIPC.payload);
			
			       
            if( INCOMPATIBLE == VerificarCompatibilidad(sNombre_NuevoDA , iCC_NuevoDA, iHH_NuevoDA, iSS_NuevoDA ) )  
            {
               infoIPC.payLoadLength=5;
			   generarStructIPC("nada", DA_INCOMPATIBLE,&infoIPC);	
               enviarIPC(iSocketDisco_temporal,&infoIPC);
               cerrarSocket(iSocketDisco_temporal);		
               printf("Disco Incompatble.Se cierra la conexion con RAID.\n");
               continue;
            }

			            
            pNodoDisco = malloc( sizeof(struct DiscoConocido) );
            strcpy(pNodoDisco->sNombre , sNombre_NuevoDA);
            pNodoDisco->iSocketHDAParaDA = iSocketDisco_temporal;
			pNodoDisco->listaPedidos = NULL;
            pNodoDisco->listaPedidos_Ult = NULL;
			pthread_mutex_init(&(pNodoDisco->mutex),NULL );

			
			int *pSocketArrayParaHDA =malloc(sizeof(int));
			int *pSocketHDAParaArray = malloc(sizeof(int));	
			
			CrearYConectarDosSockets(pSocketArrayParaHDA,pSocketHDAParaArray,obtenerValor("ipLocal"));
	
			pNodoDisco->iSocketArrayParaHDA = pSocketArrayParaHDA;
			pNodoDisco->iSocketHDAParaArray = pSocketHDAParaArray;		
			
			pNodoDisco->Sgt = NULL;
            pNodoDisco->Ant = NULL;

			
			pNodoDisco->sectores=iCC_NuevoDA*iHH_NuevoDA*iSS_NuevoDA; /*SOLO PARA HACER LA PRUEBA DEL REALLOCATE*/
			
            if(SubEstado == INCOMPLETO) { 
				pNodoDisco->Estado = LISTO;					
			}
			else{
				pNodoDisco->Estado = SINCRONIZANDO;
			}	
 
			/*Le informo al PPD que la conexion se dio de manera satisfactoria*/ 
			infoIPC.payLoadLength=5;
            generarStructIPC("nada", DA_CONECTADO_CORRECTAMENTE,&infoIPC);	
            enviarIPC(pNodoDisco->iSocketHDAParaDA,&infoIPC); 		
			/*Agrego a lista de disco*/
			agregarAListaDeDiscos(&pNodoDisco);
			
			if(SubEstado == INCOMPLETO)
            {
			   if( AgregarAVectorDiscosDeArchivoConfig(sNombre_NuevoDA) == 1) {
					/*ArrayCompleto = VERDADERO;*/
					SubEstado = SINCRONIZADO;
					Estado = INICIALIZADO;
					infoIPC.payLoadLength=5;
					generarStructIPC("nada", INICIALIZADO,&infoIPC);
					/*pNodoDisco->sectores=iCC_NuevoDA*iHH_NuevoDA*iSS_NuevoDA;*/
					
					printf("pNodoDisco->sNombre %s \n",pNodoDisco->sNombre);
					printf("pNodoDisco->sectores %i \n",pNodoDisco->sectores);
					
					enviarIPC( *(int *)pSocketParaArray,&infoIPC); /*Ya se conecto un disco:el array pasa a estar operacional.RAID OPERACIONAL*/
			   }
               else if(SubEstado == SINCRONIZADO)
                  SubEstado = SINCRONIZANDO;
            }	
			iCantidadDeDAConectados++;
            printf("Cantidad de PPD conectados %i \n",iCantidadDeDAConectados);
			pthread_create(&ID_HiloDeDisco,NULL,(void*)HiloDeDisco ,(void *)pNodoDisco);	/*se crea el hilo que va a estar comunicado al disco que se agrego*/	
			
			if(pNodoDisco->Estado == SINCRONIZANDO)
			{
				pDiscoALeer = BuscarDiscoListo();  
				if(pDiscoALeer==NULL){
					printf("No hay disco disponible para realizar la sincronizacion \n");		
				}	
				else{
					/*Sincronizar
					pDiscoALeer->Estado = SINCRONIZANDO_LEYENDO;
					pthread_create(&syncer_id,NULL, (void*)syncer,(void *)pNodoDisco);	
					*/
					pDiscoALeer->Estado=LISTO;
				}        
			}
			
			if(VerificarSITodosLosDAEstanListos() == VERDADERO)
				SubEstado = SINCRONIZADO;
		
		}       
	}
}

int  VerificarCompatibilidad(char *sNombre_NuevoDA , int iCC_NuevoDA, int iHH_NuevoDA , int iSS_NuevoDA)  /*verificar si el nombre y la cantidad de sectores coincide con lo pasado por archivo config, tmb verificar si ya se conecto un disco con ese nombre*/
{
	int i;  
	/*Me fijo si ya existe un disco con ese nombre en el array*/
	for(i = 0; i < iCantidadDiscosDeArchivoConfig ; i++)
	{	
	  if( (strcmp(sNombre_NuevoDA, pDiscosDeArchivoConfig[i].sNombre ) == 0) && (pDiscosDeArchivoConfig[i].Conectado == VERDADERO) )   
			return INCOMPATIBLE;
	}
	/*Me fijo que la cantidad de sectores del disco sea igual a la explicitada por archivo de configuracion*/
	if( (iCC_NuevoDA != glChs.cilindros) || (iHH_NuevoDA != glChs.cabezal) || (iSS_NuevoDA != glChs.sectores) )
		return INCOMPATIBLE; 

	return COMPATIBLE;
}




int AgregarAVectorDiscosDeArchivoConfig( char *sNombre_NuevoDA )  /****agrega el disco al vector, y devuelve 1 si ya se conectaron todos los discos indicados por archivo config *****/
{
    int i,iTodosLosDiscosConectados;

	iTodosLosDiscosConectados=VERDADERO;
	
    for(i = 0; i < iCantidadDiscosDeArchivoConfig ; i++)
    {	
		if(strcmp(sNombre_NuevoDA, pDiscosDeArchivoConfig[i].sNombre ) == 0) 
			pDiscosDeArchivoConfig[i].Conectado = VERDADERO;

		if(pDiscosDeArchivoConfig[i].Conectado == FALSO)
			iTodosLosDiscosConectados = FALSO;	
		
	}

    if(iTodosLosDiscosConectados = VERDADERO)
		return 1;
    else 
		return 0;

}	


int VerificarSITodosLosDAEstanListos()
{
    struct DiscoConocido * pAuxDisco;

    pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
	if (pAuxDisco->Estado != LISTO)
	    return FALSO;

	pAuxDisco = pAuxDisco->Sgt;
    }

    return VERDADERO;
}




